// Useful telnet key detection.

const compareBufStr = (buf, str) => {
  return buf.equals(Buffer.from(str.split('').map(c => c.charCodeAt(0))))
}

const telchars = {
  isSpace: buf => buf[0] === 0x20,
  isEnter: buf => buf[0] === 0x0d,
  isTab: buf => buf[0] === 0x09,
  isBackTab: buf => buf[0] === 0x1b && buf[2] === 0x5A,
  isBackspace: buf => buf[0] === 0x7F,

  // isEscape is hard because it's just send as ESC (the ANSI escape code),
  // so we need to make sure that the escape code is all on its own
  // (i.e. the length is 1)
  isEscape: buf => buf[0] === 0x1b && buf.length === 1,

  // Use this for when you'd like to detect the user confirming or issuing a
  // command, like the X button on your PlayStation controller, or the mouse
  // when you click on a button.
  isSelect: buf => telchars.isSpace(buf) || telchars.isEnter(buf),

  // Use this for when you'd like to detect the user cancelling an action,
  // like the O button on your PlayStation controller, or the Escape key on
  // your keyboard.
  isCancel: buf => telchars.isEscape(buf),

  isUp: buf => buf[0] === 0x1b && buf[2] === 0x41,
  isDown: buf => buf[0] === 0x1b && buf[2] === 0x42,
  isRight: buf => buf[0] === 0x1b && buf[2] === 0x43,
  isLeft: buf => buf[0] === 0x1b && buf[2] === 0x44,

  // Mouse constants!
  mapMouseActionNum: num => {
    let button = null

    if (num & 64) {
      if (num & 1) button = 'scroll-down'
      else button = 'scroll-up'
    } else {
      const drag = num & 32
      const bits = num & 3
      if (bits === 0) button = 'left'
      else if (bits === 1) button = 'middle'
      else if (bits === 2) button = 'right'
      else if (bits === 3) button = 'release'
      if (drag) {
        button = 'drag-' + button
      }
    }

    const shift = !!(num & 4)
    const ctrl = !!(num & 16)

    return {button, shift, ctrl}
  },

  isMouse: buf => buf[0] === 0x1b && buf[2] === 0x4d,
  parseMouse: buf => {
    if (!telchars.isMouse(buf)) {
      return null
    }

    const actionNum = buf[3] - 32
    const col = buf[4] - 32
    const line = buf[5] - 32

    const { button, shift, ctrl } = telchars.mapMouseActionNum(actionNum)

    return {button, shift, ctrl, col, line, actionNum}
  },

  isShiftUp: buf => compareBufStr(buf, '\x1b[1;2A'),
  isShiftDown: buf => compareBufStr(buf, '\x1b[1;2B'),
  isShiftRight: buf => compareBufStr(buf, '\x1b[1;2C'),
  isShiftLeft: buf => compareBufStr(buf, '\x1b[1;2D'),

  isMetaUp: buf => compareBufStr(buf, '\x1b[1;3A'),
  isMetaDown: buf => compareBufStr(buf, '\x1b[1;3B'),
  isMetaRight: buf => compareBufStr(buf, '\x1b[1;3C'),
  isMetaLeft: buf => compareBufStr(buf, '\x1b[1;3D'),

  isControlUp: buf => compareBufStr(buf, '\x1b[1;5A'),
  isControlDown: buf => compareBufStr(buf, '\x1b[1;5B'),
  isControlRight: buf => compareBufStr(buf, '\x1b[1;5C'),
  isControlLeft: buf => compareBufStr(buf, '\x1b[1;5D'),

  isHome: buf => compareBufStr(buf, '\x1b[1~'),
  isInsert: buf => compareBufStr(buf, '\x1b[2~'),
  isDelete: buf => compareBufStr(buf, '\x1b[3~'),
  isEnd: buf => compareBufStr(buf, '\x1b[4~'),
  isPageUp: buf => compareBufStr(buf, '\x1b[5~'),
  isPageDown: buf => compareBufStr(buf, '\x1b[6~'),

  isCaselessLetter: (buf, letter) => compareBufStr(buf, letter.toLowerCase()) || compareBufStr(buf, letter.toUpperCase()),
  isCharacter: (buf, char) => compareBufStr(buf, char),
}

export default telchars
